#include <stdio.h>
#include "esp_log.h"
#include "i2c_custom.h"
#include "axp2101.h"
#include "freertos/semphr.h"

const static char *TAG = "axp2101";

static SemaphoreHandle_t xAxp2101Semaphore;

static esp_err_t axp2101_write_reg(uint8_t write_reg, uint8_t write_buffer)
{
    uint8_t wr_buff[2] = {write_reg, write_buffer};
    return i2c_master_write_to_device(AXP2101_I2C_MASTET_NUM, AXP2101_ADDRSSS, wr_buff, 2, portTICK_PERIOD_MS);
}

static esp_err_t axp2101_read_reg(uint8_t read_reg, uint8_t *read_buffer)
{
    return i2c_master_write_read_device(AXP2101_I2C_MASTET_NUM, AXP2101_ADDRSSS, &read_reg, 1, read_buffer, 1, portTICK_PERIOD_MS);
}

/**
 * @brief axp2101_i2c_master_init
 *
 * @param sda
 * @param scl
 * @return esp_err_t
 */
esp_err_t axp2101_i2c_master_init(int sda, int scl)
{
    i2c_config_t i2c_config = i2c_conf_func();
    // i2c_param_config(AXP2101_I2C_MASTET_NUM, &i2c_config);
    // i2c_driver_install(AXP2101_I2C_MASTET_NUM, I2C_MODE_MASTER, 0, 0, 0);
    i2c_param_config(AXP2101_I2C_MASTET_NUM, &i2c_config);
    return i2c_driver_install(AXP2101_I2C_MASTET_NUM, I2C_MODE_MASTER, 0, 0, 0);
}

esp_err_t axp2101_gpio_init(void)
{
    esp_err_t ret = ESP_OK;

    /* axp2101 init gpio start */
    ret |= gpio_reset_pin(AXP2101_IRQ_PIN);
    ret |= gpio_set_direction(AXP2101_IRQ_PIN, GPIO_MODE_INPUT);
    ret |= gpio_reset_pin(AXP2101_PWROK_PIN);
    ret |= gpio_set_direction(AXP2101_PWROK_PIN, GPIO_MODE_INPUT);
    /* axp2101 init gpio end */

    return ret;
}

esp_err_t axp2101_init(void)
{
    uint8_t init_buff;
    esp_err_t ret = ESP_OK;

    /* axp2101 gpio init start */
    // ret |= axp2101_gpio_init();
    /* axp2101 gpio init end */

    /* read charging limit current start  */
    ret |= axp2101_read_reg(ICC_CHARGER_SETTING_REG, &init_buff);
    ESP_LOGI(TAG, "ICC charger setting:[%#X].", init_buff);
    /* read charging limit current end  */

    /* limit charging current start */
    init_buff = 0b00001000; /* 200mA */
    ret |= axp2101_write_reg(ICC_CHARGER_SETTING_REG, init_buff);
    ret |= axp2101_read_reg(ICC_CHARGER_SETTING_REG, &init_buff);
    ESP_LOGI(TAG, "ICC change charger setting:[%#X].", init_buff);
    /* limit charging current end */

    /*
    ret |= axp2101_read_reg(0x50, &init_buff);
    ESP_LOGI(TAG, "TS fuc:[%#X].", init_buff);
    init_buff = 0x10;
    ret |= axp2101_write_reg(0x50, init_buff);
    ret |= axp2101_read_reg(0x50, &init_buff);
    ESP_LOGI(TAG, "TS set fuc:[%#X].", init_buff);
    */

    ret |= axp2101_read_reg(PMU_STATUS_1_REG, &init_buff);
    ESP_LOGI(TAG, "VBUS good:[%#X].", init_buff);

    /*
    ret |= axp2101_read_reg(0x18, &init_buff);
    ESP_LOGI(TAG, "charger en:[%#X].", init_buff);
    init_buff = init_buff | 0b000000010;
    ret |= axp2101_write_reg(0x18, init_buff);
    ret |= axp2101_read_reg(0x18, &init_buff);
    ESP_LOGI(TAG, "Charger set en:[%#X].", init_buff);
    */

    ret |= axp2101_read_reg(BATFET_CONTROL_REG, &init_buff);
    ESP_LOGI(TAG, "BATFET dis:[%#X].", init_buff);

    /*
    init_buff = init_buff & 0b11111011;
    ret |= axp2101_read_reg(0x12, &init_buff);
    ESP_LOGI(TAG, "BATFET set dis:[%#X].", init_buff);
    */

    return ret;
}

uint8_t axp2101_bat_level(void)
{
    uint8_t bat_level_buff;
    axp2101_read_reg(BATTERY_PER_DATA_REG, &bat_level_buff);
#if AXP2101_DEBUG
    ESP_LOGI(TAG, "BAT level:[%d%%]", bat_level_buff);
#endif

    return bat_level_buff;
}

bool axp2101_is_charging(void)
{
    uint8_t is_charging_buff;

    axp2101_read_reg(PMU_STATUS_2_REG, &is_charging_buff);
    if ((is_charging_buff & 0b01100000) == 0b00100000)
    {
#if AXP2101_DEBUG
        ESP_LOGI(TAG, "axp2101 is charging.[%#X]", is_charging_buff);
#endif
        return true;
    }
    else
    {
#if AXP2101_DEBUG
        ESP_LOGI(TAG, "axp2101 isn't charging.[%#X]", is_charging_buff);
#endif
        return false;
    }
}

bool axp2101_is_charge_done(void)
{
    uint8_t is_charge_done_buff;

    axp2101_read_reg(PMU_STATUS_2_REG, &is_charge_done_buff);
    if ((is_charge_done_buff & 0b00000111) == 0b00000100)
    {
#if AXP2101_DEBUG
        ESP_LOGI(TAG, "axp2101 is charge done.[%#X]", is_charge_done_buff);
#endif
        return true;
    }
    else
    {
#if AXP2101_DEBUG
        ESP_LOGI(TAG, "axp2101 isn't charge done.[%#X]", is_charge_done_buff);
#endif
        return false;
    }
}

void axp2101_pwr_off(void)
{
    uint8_t pwr_off_buff;

    /* read PMU common configuration start */
    axp2101_read_reg(PMU_COMMON_COFIG_REG, &pwr_off_buff);
#if AXP2101_DEBUG
    ESP_LOGI(TAG, "common configuration:[%#X]", pwr_off_buff);
#endif
    /* read PMU common configuration end */

    /* soft pwr off start */
    ESP_ERROR_CHECK(axp2101_write_reg(PMU_COMMON_COFIG_REG, pwr_off_buff | 0x01));
    /* soft pwr off end */
}

void axp2101_task(void *arg)
{
    /* axp2101 init start */
    xAxp2101Semaphore = xSemaphoreCreateMutex();
    ESP_ERROR_CHECK(axp2101_init());
    /* axp2101 init end */
    while (1)
    {
        if (pdTRUE == xSemaphoreTake(xAxp2101Semaphore, portMAX_DELAY))
        {
            /* get vbat status and level start */
            axp2101_is_charging();
            axp2101_is_charge_done();
            axp2101_bat_level();
            /* get vbat status and level end */
            xSemaphoreGive(xAxp2101Semaphore);
        }

        // ESP_LOGI(TAG, "IRQ status:[%d]", gpio_get_level(AXP2101_IRQ_PIN));
        // count += 1;
        // ESP_LOGI(TAG, "axp2101 count:[%d]", count);
        // if (count >= 10)
        // {
        //     ESP_LOGI(TAG, "axp2101 off");
        //     axp2101_pwr_off();
        // }

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
